<?php
/**
 *
 * @author Martin "DevelX" Jurča
 */
class Polls extends Module {
    protected $name = 'Polls';
    protected $administrable = true;
    protected $version = '2010-02-14';
    protected $details = array('dependencies' => array(),
            'observes' => array('Polls'));

    const POLLS = 'polls';
    const OPTIONS = 'pollsOptions';
    const VOTES = 'pollsVotes';
    
    protected $pollsTable = array('id' => 'iPN+', 'name' => 's', 'created' =>
            'iN', 'lastEdit' => 'i', 'openUntil' => 'iN', 'approved' => 'iN',
            'type' => 'iN', 'minVotes' => 'iN', 'maxVotes' => 'iN',
            'defaultVotes' => 'iN');
    protected $optionsTable = array('id' => 'iPN+', 'poll' => 'iNI', 'name' =>
            's');
    protected $votesTable = array('id' => 'iPN+', 'option' => 'iNI', 'user' =>
            'iNI', 'ip' => "v'15'", 'time' => 'iN');

    protected $pollRecord = array('table' => self::POLLS, 'data' => array());
    protected $optionRecord = array('table' => self::OPTIONS, 'data' =>
            array());
    protected $voteRecord = array('table' => self::VOTES, 'data' => array());

    public function __construct(DBAPI $dbapi, RecordList $recordList) {
        parent::__construct($dbapi, $recordList);
        $this->pollRecord['properties'] = $this->pollsTable;
        $this->optionRecord['properties'] = $this->optionsTable;
        $this->voteRecord['properties'] = $this->votesTable;
    }

    public function install() {
        Modules::getModule('Users')->registerRights($this->getName(),
                $this->getRights());
        return $this->dbdriver->createTable(self::POLLS, $this->pollsTable) &&
                $this->dbdriver->createTable(self::OPTIONS,
                $this->optionsTable) && $this->dbdriver->
                createTable(self::VOTES, $this->votesTable);
    }

    public function uninstall() {
        return $this->dbdriver->dropTable(self::POLLS) &&
                $this->dbdriver->dropTable(self::OPTIONS) &&
                $this->dbdriver->dropTable(self::VOTES);
    }

    public function onAdd(&$details, $source) {
        if (!$this->hasRights('create poll')) {
            return;
        }
        $poll = $this->pollRecord;
        $poll['data'] = array('name' => $details['name'], 'created' => time(),
                'openUntil' => strtotime($details['openUntil']), 'approved' =>
                0, 'type' => $details['type'], 'minVotes' =>
                (int) $details['minVotes'], 'maxVotes' =>
                (int) $details['maxVotes'], 'defaultVotes' =>
                (int) $details['defaultVotes']);
        $this->dbapi->save($poll);
        if (!isset($details['option'])) {
            return;
        }
        foreach ($details['option'] as $option) {
            if ($option != '') {
                $opt = $this->optionRecord;
                $opt['data'] = array('poll' => $poll['data']['id'], 'name' =>
                        $option);
                $this->dbapi->save($opt);
            }
        }
    }

    public function onShow(&$details, $source) {
        Kernel::setRedirect($this->getLink());
        if (!$this->hasRights('approve')) {
            return;
        }
        $poll = $this->pollRecord;
        $poll['data']['id'] = (int) $details;
        $this->dbapi->refresh($poll);
        $poll['data']['approved'] = 1;
        $this->dbapi->update($poll);
        Modules::getModule('RSS')->pushItem($this->lang('newPoll') .
                $poll['data']['name'], 'http://' . Config::DOMAIN_NAME .
                Config::WEB_DIR . '/ankety/', $this->lang('newPoll') .
                $poll['data']['name']);
    }

    public function onHide(&$details, $source) {
        Kernel::setRedirect($this->getLink());
        if (!$this->hasRights('approve')) {
            return;
        }
        $poll = $this->pollRecord;
        $poll['data']['id'] = (int) $details;
        $this->dbapi->refresh($poll);
        $poll['data']['approved'] = 0;
        $this->dbapi->update($poll);
    }

    public function onDelete(&$details, $source) {
        Kernel::setRedirect($this->getLink());
        if (!$this->hasRights('delete poll')) {
            return;
        }
        $cols = array('id');
        $options = &$this->dbdriver->select(self::OPTIONS, $cols, '`poll` = ' .
                (int) $details);
        if (count($options) > 0) {
            foreach ($options as &$option) {
                $option = $option['id'];
            }
            $this->dbdriver->delete(self::VOTES, '`option` IN (' .
                    implode(',', $options) . ')');
        }
        $this->dbdriver->delete(self::OPTIONS, '`poll` = ' . (int) $details);
        $this->dbdriver->delete(self::POLLS, '`id` = ' . (int) $details);
    }

    public function onEdit(&$details, $source) {
        if (!$this->hasRights('edit poll')) {
            return;
        }
        $poll = $this->pollRecord;
        $poll['data']['id'] = (int) $details['id'];
        $this->dbapi->refresh($poll);
        $poll['data']['name'] = $details['name'];
        $poll['data']['lastEdit'] = time();
        $poll['data']['openUntil'] = strtotime($details['openUntil']);
        $this->dbapi->update($poll);
        $cols = array('id');
        $options = &$this->dbdriver->select(self::OPTIONS, $cols,
                '`poll` = ' . (int) $details['id']);
        $delOptions = array();
        foreach ($options as &$option) {
            $delOptions[$option['id']] = true;
        }
        if (isset($details['oldOption'])) {
            foreach ($details['oldOption'] as $id => $name) {
                if ($name != '') {
                    $option = $this->optionRecord;
                    $option['data']['id'] = (int) $id;
                    $this->dbapi->refresh($option);
                    $option['data']['name'] = $name;
                    $this->dbapi->update($option);
                    unset($delOptions[$id]);
                }
            }
        }
        if (count($delOptions) > 0) {
            $this->dbdriver->delete(self::OPTIONS, '`id` IN (' .
                    implode(',', array_keys($delOptions)). ')');
        }
        if (isset($details['option'])) {
            foreach ($details['option'] as $name) {
                if ($name != '') {
                    $option = $this->optionRecord;
                    $option['data'] = array('poll' => (int) $details['id'],
                            'name' => $name);
                    $this->dbapi->save($option);
                }
            }
        }
    }

    public function onMultiVode(&$details) {
        $user = Modules::getModule('Users')->getUser();
        $cols = array('poll');
        foreach ($details['option'] as $id => $count) {
            $poll = &$this->dbdriver->select(self::OPTIONS, $cols, '`id` = ' .
                    (int) $id);
            if (!isset($poll[0])) {
                return;
            }
            $poll = $poll[0]['poll'];
            if (!$this->hasRights('vote') || $this->hasVoted($poll)) {
                return;
            }
        }
        foreach ($details['option'] as $id => $count) {
            for ($i = 0; $i < $count; $i++) {
                $vote = $this->voteRecord;
                $vote['data'] = array('option' => (int) $id, 'user' =>
                        $user['data']['id'], 'ip' => $_SERVER['REMOTE_ADDR'],
                        'time' => time());
                $this->dbapi->save($vote);
            }
        }
    }

    public function onVote(&$details, $source) {
        Kernel::setRedirect($this->getLink());
        if (is_array($details) && isset($details['type'])) {
            $this->onMultiVode($details);
            return;
        }
        $cols = array('poll');
        $poll = &$this->dbdriver->select(self::OPTIONS, $cols, '`id` = ' .
                (int) $details);
        if (!isset($poll[0])) {
            return;
        }
        $poll = $poll[0]['poll'];
        if (!$this->hasRights('vote') || $this->hasVoted($poll)) {
            return;
        }
        $user = Modules::getModule('Users')->getUser();
        $vote = $this->voteRecord;
        $vote['data'] = array('option' => (int) $details, 'user' =>
                $user['data']['id'], 'ip' => $_SERVER['REMOTE_ADDR'], 'time' =>
                time());
        $this->dbapi->save($vote);
    }

    public function initDisplayAdminAdd() {
        Modules::getModule('View')->addJS(Config::WEB_DIR .
                $this->getDir('/pollform.js', true));
    }

    public function initDisplayAdminEdit() {
        $this->initDisplayAdminAdd();
    }

    public function displayPublicPoll($id) {
        $canVote = $this->hasRights('vote');
        $voted = $this->hasVoted($id);
        $cols = array('name', 'openUntil', 'approved', 'type', 'minVotes',
                'maxVotes', 'defaultVotes');
        $poll = &$this->dbdriver->select(self::POLLS, $cols,
                '`id` = ' . (int) $id);
        $poll = $poll[0];
        $user = Modules::getModule('Users')->getUser();
        if (($poll['approved'] == 0) &&
                ($user['data']['login'] == '<guest>')) {
            return;
        }
        $cols = array('id', 'name');
        $order = array('id');
        $options = &$this->dbdriver->select(self::OPTIONS, $cols,
                '`poll` = ' . (int) $id, $order);
        $total = 0;
        foreach ($options as &$option) {
            $option['vote'] = $this->getLink($option['id'], 'vote');
            $option['votes'] = $this->getVotesCount($option['id']);
            $total += $option['votes'];
        }
        $count = count($options);
        foreach ($options as &$option) {
            $option['percent'] = $total > 0 ?
                    ($option['votes'] / $total) * 100 : 100 / $count;
        }
        $params = array('poll' => $poll, 'options' => $options, 'canVote' =>
                $canVote && !$voted, 'total' => $total, 'formStart' =>
                $this->getForm('vote', true, 'Polls'));
        $this->template('poll', $params);
    }

    public function displayPublicIndex() {
        $user = Modules::getModule('Users')->getUser();
        $user = $user['data']['login'] != '<guest>';
        $cols = array('id');
        $order = array('!id');
        $polls = &$this->dbdriver->select(self::POLLS, $cols,
                '`openUntil` > ' . time() . ($user ? '' :
                ' AND `approved` = 1'), $order);
        foreach ($polls as $poll) {
            $this->displayPublicPoll($poll['id']);
        }
    }

    public function displayAdminEdit($id) {
        if (!$this->hasRights('edit poll')) {
            return;
        }
        $poll = $this->pollRecord;
        $poll['data']['id'] = (int) $id;
        $this->dbapi->refresh($poll);
        $cols = array('id', 'name');
        $order = array('id');
        $options = &$this->dbdriver->select(self::OPTIONS, $cols,
                '`poll` = ' . (int) $id, $order);
        $form = array('start' => 'edit', 'legend' => 'editForm', 'submit' =>
                'save', 'items' => array(array('name' => 'name', 'label' =>
                'name', 'value' => $poll['data']['name']), array('name' =>
                'openUntil', 'label' => 'openUntil', 'value' => date("d.m.Y",
                $poll['data']['openUntil'])), array('type' => 'html',
                'label' => 'options'), array('name' => 'id', 'type' =>
                'hidden', 'value' => (int) $id)));
        foreach ($options as &$option) {
            $form['items'][] = array('label' => 'name', 'name' =>
                    "oldOption[{$option['id']}]", 'value' => $option['name']);
        }
        $form['items'][] = array('type' => 'html','html' => '<a href="#">' .
                $this->lang('addOption') . '</a>');
        $this->prepareFormData($form);
        $params = array('form' => &$form);
        $this->template('form', $params);
    }

    public function displayAdminIndex() {
        $cols = array('id', 'name', 'approved', 'openUntil');
        $order = array('!id');
        $polls = &$this->dbdriver->select(self::POLLS, $cols, '', $order);
        foreach ($polls as &$poll) {
            $poll['edit'] = $this->getLink("edit/{$poll['id']}/");
            $poll['show'] = $this->getLink($poll['id'], 'show');
            $poll['hide'] = $this->getLink($poll['id'], 'hide');
            $poll['delete'] = $this->getLink($poll['id'], 'delete');
        }
        $params = array('polls' => &$polls);
        $this->template('list', $params);
    }

    public function displayAdminAdd() {
        if (!$this->hasRights('create poll')) {
            return;
        }
        $form = array('start' => 'add', 'legend' => 'addForm', 'submit' =>
                'add', 'items' => array(array('name' => 'name', 'label' =>
                'name'), array('name' => 'openUntil', 'label' => 'openUntil'),
                array('name' => 'type', 'type' => 'select', 'options' =>
                    array(0 => $this->lang('normalPoll'), 1 => $this->
                    lang('scoringPoll')), 'label' => 'type'),
                array('name' => 'minVotes', 'label' => 'minVotes'),
                array('name' => 'maxVotes', 'label' => 'maxVotes'),
                array('name' => 'defaultVotes', 'label' => 'defaultVotes'),
                array('type' => 'html', 'label' => 'options'), array('name' =>
                'option[]', 'label' => 'name'), array('name' => 'option[]',
                'label' => 'name'), array('type' => 'html', 'html' =>
                '<a href="#">' . $this->lang('addOption') . '</a>')));
        $this->prepareFormData($form);
        $params = array('form' => &$form);
        $this->template('form', $params);
    }

    public function displayAdminSubmenu() {
        $menu = array(array('label' => 'list', 'href' => ''),
                array('label' => 'add', 'href' => 'add/'));
        $this->prepareMenu($menu);
        $params = array('menu' => &$menu);
        $this->template('submenu', $params);
    }

    private function getVotesCount($option) {
        $cols = array('COUNT(`id`)');
        $count = &$this->dbdriver->select(self::VOTES, $cols, '`option` = ' .
                (int) $option);
        return $count[0]['COUNT(`id`)'];
    }

    private function hasVoted($poll) {
        $user = Modules::getModule('Users')->getUser();
        $ip = $_SERVER['REMOTE_ADDR'];
        $cols = array('id');
        $options = &$this->dbdriver->select(self::OPTIONS, $cols,
                '`poll` = ' . (int) $poll);
        foreach ($options as &$option) {
            $option = $option['id'];
        }
        if (!isset($options[0])) {
            return false;
        }
        $cols = array('id');
        $check = &$this->dbdriver->select(self::VOTES, $cols, '`option` IN (' .
                implode(',', $options) . ') AND ((`user` = ' .
                $user['data']['id'] . ' AND `user` != 2) OR ' .
                '(`user` = 2 AND `ip` = \'' .
                $this->dbdriver->escapeString($ip) . '\'))');
        return isset($check[0]);
    }
}
?>
